package one.deploy.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import one.deploy.dto.ApplicationListResponse;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ServiceConfigReader implements IServiceConfigReader {
    public static final Pattern pattern = Pattern.compile("(<meta name=\"version\" content=\"(.+?)\">)|(<meta name=version content=(.+?)>)");
    private Yaml yaml = new Yaml();
    private CommandExecuter commandExecuter = new CommandExecuter();
    private IVersionReader versionReader = new DefaultVersionReader();
    private String deploy;

    private Config config;
    private Map<String, App> appMap;

    public ServiceConfigReader(String deploy) {
        this.deploy = deploy;
    }

    @Override
    public void reload() {
        if (Objects.isNull(config)) {
            load();
        }

        //刷新配置文件
        commandExecuter.exe(config.getConfigPath(), null);

        //加载配置
        load();
    }

    @Override
    public Config getConfig() {
        return config;
    }

    @Override
    public List<App> getApps() {
        return new ArrayList<>(appMap.values());
    }

    @Override
    public App getApp(String name) {
        return appMap.get(name);
    }

    @Override
    public Action getAction(String name, String commandKey) {
        Map<String, IServiceConfigReader.Action> commonds = appMap.get(name).getActions();
        return commonds.get(commandKey);
    }

    @Override
    public List<ApplicationListResponse.Instance> getInstances(String name) {
        App app = getApp(name);
        return getInstances(app);
    }

    @Override
    public List<ApplicationListResponse.Instance> getInstances(App app) {
        String url = app.getInstanceUrl();
        if (StringUtils.isBlank(url)) return null;

        String json = null;
        try {
            json = HttpUtil.get(url, 1000);
            if (StringUtils.isBlank(json)) return null;

            AppInfo appInfo = null;
            if (json.contains("<html ") || json.contains("<html>")) {
                Matcher matcher = pattern.matcher(json);
                if (matcher.find()) {
                    String result = StringUtils.defaultIfBlank(matcher.group(2), matcher.group(4));

                    appInfo = new AppInfo();
                    Application application = new Application();
                    application.setInstance(Lists.newArrayList(ApplicationListResponse.Instance.builder()
                            .instanceId(result)
                            .version(result)
                            .status("UP")
                            .build()));
                    appInfo.setApplication(application);
                }
            } else {
                appInfo = JSONUtil.toBean(json, AppInfo.class);
                if (CollUtil.isNotEmpty(appInfo.getApplication().getInstance())) {
                    for (ApplicationListResponse.Instance instance : appInfo.getApplication().getInstance()) {
                        String[] ids = instance.getInstanceId().split(":");
                        instance.setVersion(ids[ids.length - 1]);
                    }
                }
            }

            if (Objects.nonNull(appInfo)) {
                return appInfo.getApplication().getInstance();
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        log.error("url({}) result:{}", url, json);
        return null;
    }

    private void load() {

        InputStream inputStream = null;
        File file = new File(deploy);
        try {
            inputStream = new FileInputStream(file);
            Map<String, Object> ret = (Map<String, Object>) yaml.load(inputStream);
            config = Config.builder()
                    .workspacePath((String) ret.get("projectBootPath"))
                    .configPath((String) ret.get("cdBootPath"))
                    .build();
            config.setWorkspacePath(StringUtils.defaultIfBlank(config.getWorkspacePath(), StringUtils.EMPTY));

            appMap = new LinkedHashMap<>();
            String bootPath = StringUtils.isBlank(config.getWorkspacePath()) ? StringUtils.EMPTY : config.getWorkspacePath() + File.separator;
            List<LinkedHashMap<String, Object>> services = (List<LinkedHashMap<String, Object>>) ret.get("services");
            for (LinkedHashMap<String, Object> map : services) {
                App serviceConfig = new App();
                serviceConfig.setName((String) map.get("name"));
                String workspacePath = (String) map.get("path");
                serviceConfig.setBootPath(StringUtils.startsWith(workspacePath, "file:") ? StringUtils.replace(workspacePath, "file:", "") : bootPath + map.get("path"));
                serviceConfig.setLastVersionUrl(serviceConfig.getBootPath() + StringUtils.defaultIfBlank((String) map.get("lastVersionUrl"), "/pom.xml"));
                serviceConfig.setInstanceUrl((String) map.get("instanceUrl"));
                serviceConfig.setUpdateReload(BooleanUtils.toBooleanDefaultIfNull((Boolean) map.get("updateReload"), false));
                Map<String, IServiceConfigReader.Action> actionMap = new LinkedHashMap<>();
                for (Map.Entry<String, String> entry : ((Map<String, String>) map.get("config")).entrySet()) {
                    String url = entry.getValue().startsWith("http://") ? entry.getValue()
                            : "action?name=" + serviceConfig.getName() + "&commandKey=" + entry.getKey();
                    actionMap.put(entry.getKey(), IServiceConfigReader.Action.builder()
                            .name(entry.getKey())
                            .txt(entry.getValue())
                            .content(url)
                            .build());
                }
                serviceConfig.setActions(actionMap);

                //刷新项目
                if (serviceConfig.isUpdateReload()) {
                    commandExecuter.exe(config.getConfigPath() + " -n " + serviceConfig.getBootPath(), null);
                }
                serviceConfig.setVersion(versionReader.getLast(serviceConfig.getLastVersionUrl()));

                appMap.put(serviceConfig.getName(), serviceConfig);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (Objects.nonNull(inputStream)) {
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        log.info("yml reload");
    }
}
